#include "invoker.h"

#include <stdlib.h>
#include <string.h>

Invoker* invoker_new(Image* img)
{
	Invoker* ivk = calloc(1, sizeof(Invoker));
	ivk->img = img;
	ivk->undo_stack = NULL;
	ivk->redo_stack = NULL;
	ivk->us_size = ivk->rs_size = 0;
	return ivk;
}


int invoker_execute(Invoker* ivk, Command* _cmd)
{
	// copy command
	Command* cmd = malloc(sizeof(Command));
	memcpy(cmd, _cmd, sizeof(Command));

	// execute command
	int r = command_functions[cmd->command].cmd(ivk->img, cmd);

	if(r)
	{
		// add to UNDO stack
		ivk->us_size++;
		ivk->undo_stack = realloc(ivk->undo_stack, 
				ivk->us_size * sizeof(Command));
		ivk->undo_stack[ivk->us_size-1] = cmd;

		// clear REDO stack
		if(ivk->rs_size > 0)
		{
			int i;
			for(i=0; i<ivk->rs_size; i++)
				command_free(ivk->redo_stack[i]);
			free(ivk->redo_stack);
			ivk->redo_stack = NULL;
			ivk->rs_size = 0;
		}
	}

	return r;
}


int invoker_undo(Invoker* ivk)
{
	if(ivk->us_size == 0)
		return 0;

	Command* cmd = ivk->undo_stack[ivk->us_size-1];
	int r = command_functions[cmd->command].cmd_undo(ivk->img, cmd);

	if(r)
	{
		// add to REDO stack
		ivk->rs_size++;
		ivk->redo_stack = realloc(ivk->redo_stack, 
				ivk->rs_size * sizeof(Command));
		ivk->redo_stack[ivk->rs_size-1] = cmd;

		// remove from UNDO stack
		ivk->us_size--;
		if(ivk->us_size == 0)
		{
			free(ivk->undo_stack);
			ivk->undo_stack = NULL;
		}
		else
			ivk->undo_stack = realloc(ivk->undo_stack,
					ivk->us_size * sizeof(Command));
		// cmd is not freed because it goes to the REDO stack
	}

	return r;
}


int invoker_redo(Invoker *ivk)
{
	if(ivk->rs_size == 0)
		return 0;

	// remove command from REDO stack
	Command* cmd = ivk->redo_stack[ivk->rs_size-1];
	ivk->rs_size--;
	if(ivk->rs_size == 0)
	{
		free(ivk->redo_stack);
		ivk->redo_stack = NULL;
	}
	else
		ivk->redo_stack = realloc(ivk->redo_stack,
				ivk->rs_size * sizeof(Command));

	// execute it
	invoker_execute(ivk, cmd);
	command_free(cmd);

	return -1;
}


void invoker_delete(Invoker* ivk)
{
	int i;
	if(ivk->us_size > 0)
	{
		for(i=0; i<ivk->us_size; i++)
			command_free(ivk->undo_stack[i]);
		free(ivk->undo_stack);
	}
	if(ivk->rs_size > 0)
	{
		for(i=0; i<ivk->rs_size; i++)
			command_free(ivk->redo_stack[i]);
		free(ivk->redo_stack);
	}
	free(ivk);
}
